package snapex.invoice.controller;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.json.JacksonJsonParser;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import snapex.core.model.Invoice;
import snapex.core.model.InvoiceCheck;
import snapex.core.model.InvoiceImage;
import snapex.core.model.Result;
import snapex.feeclouds.controller.MainController;
import snapex.feeclouds.model.resp.ErrorResps;
import snapex.invoice.service.IInvoiceService;

@Slf4j
@SuppressWarnings("all")
@RestController
public class InvoiceController extends MainController {

	@Autowired
	IInvoiceService invoiceService;
    /**
     * 此Demo未校验发票类型,请自行判断是传金额还是传校验码
     * invoiceCode: 发票代码 必填
     * invoiceNumber: 发票号码 必填
     * invoiceDate: 开票日期 必填
     * amount: 金额 非必填
     * checkCode: 校验码 非必填
     */
	@ApiOperation(value="check invoice")
	@ApiImplicitParams({
		 @ApiImplicitParam(name = "invoiceCode", value = "发票代码 必填 044001900111", required = true, dataType = "String"),
		 @ApiImplicitParam(name = "invoiceNumber", value = "发票号码 必填 79784587", required = true, dataType = "String"),
		 @ApiImplicitParam(name = "invoiceDate", value = "开票日期 必填 2019-09-24", required = true, dataType = "String"),
		 @ApiImplicitParam(name = "amount", value = "金额 选填 60.38 ", required = false, dataType = "String"),
		 @ApiImplicitParam(name = "checkCode", value = "校验码 选填 14446697673732379007", required = false, dataType = "String")
	 })	
	@GetMapping("/invoice/check")
    public Object checkInvoice(@RequestParam("invoiceCode") String invoiceCode,
                        @RequestParam("invoiceNumber") String invoiceNumber,
                        @RequestParam("invoiceDate") String invoiceDate,
                        String amount,String checkCode) {
    	    	
		InvoiceCheck invoiceCheck = invoiceService.getInvoiceCheck(invoiceCode, invoiceNumber, invoiceDate);		
		if(invoiceCheck != null) {			
			return new JacksonJsonParser().parseMap(invoiceCheck.getCheckResult());
		}
		
    	Object response = this.check(invoiceCode, invoiceNumber, invoiceDate, amount, checkCode);
    	if(response instanceof ErrorResps) {
    		return response;
    	}else {
    		String content = (String)response;
    		response = new JacksonJsonParser().parseMap(content);
    		
    		Map<String,Map<String,Object>> map = (Map<String,Map<String,Object>>)response;
			
			log.debug("********response:{}",response);
			if(map.get("data") != null) {
							
				String invoiceId = (String)map.get("data").get("invoiceid");
				BigDecimal amountBeforeTax = BigDecimal.valueOf(Double.valueOf(((Map)map.get("data").get("invoice")).get("je").toString()));
				BigDecimal amountAfterTax = BigDecimal.valueOf(Double.valueOf(((Map)map.get("data").get("invoice")).get("jshj").toString()));
				   
				Invoice invoice = invoiceService.getInvoice(invoiceCode, invoiceNumber, invoiceDate);
				if(invoice == null) {
					invoice = new Invoice();
					invoice.setInvoiceId(invoiceId);
					invoice.setInvoiceCode(invoiceCode);
					invoice.setInvoiceNumber(invoiceNumber);
					invoice.setInvoiceDate(invoiceDate);
					invoice.setAmount(amountBeforeTax);
					invoice.setTaxAmount(amountAfterTax);
					invoice.setCheckCode(checkCode);
					
				}else {
					if(!StringUtils.isEmpty(invoice.getInvoiceId())) {
						invoiceId = invoice.getInvoiceId();
					}
					invoice.setAmount(amountBeforeTax);
					invoice.setTaxAmount(amountAfterTax);
				}

				InvoiceCheck invoiceCheckRes = new InvoiceCheck();
				invoiceCheckRes.setInvoiceId(invoiceId);
				invoiceCheckRes.setCheckResult(content);
				
				invoiceService.saveInvoiceCheck(invoice,invoiceCheckRes);    			
			}
    	}
    	
    	return response;
    }
	
	@ApiOperation(value="add invoice")	
	@PostMapping("/invoice/add")
	@PreAuthorize("hasAuthority('R_O_INV_Verify')")
    public ResponseEntity<Result> addInvoice(@RequestBody Invoice invoice) {
			
		return ResponseEntity.ok(Result.success().data(invoiceService.saveInvoice(invoice)));
	}	
	
	@ApiOperation(value="Get invoice")
	@ApiImplicitParam(name = "invoiceId", value = "invoice id", required = true, dataType = "String")
	@GetMapping(value = "/invoice/{invoiceId}", produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
	@PreAuthorize("hasAuthority('R_O_INV_Verify')")
	public ResponseEntity<Result> getInvoice(@PathVariable String invoiceId) {
		
		return ResponseEntity.ok(Result.success().data(invoiceService.getInvoice(invoiceId)));			
	}

	
	@ApiOperation(value="Upload invoice image")
	@PostMapping(value = "/invoice/{invoiceId}/upload", produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
	@PreAuthorize("hasAuthority('R_O_INV_Verify')")
	public ResponseEntity<Result> uploadImage(@PathVariable String invoiceId, @RequestParam("invoice") MultipartFile invoice) throws IOException {
		
		if(invoice.isEmpty()) {
			ResponseEntity.ok(Result.failure().id(invoiceId));	
		}
        
		invoiceService.saveInvoiceImage(invoiceId, invoice.getBytes(),invoice.getContentType().substring("image/".length()));
		
		return ResponseEntity.ok(Result.success().id(invoiceId));			
	}
	
	@Value("${snapex.accessKeyValidPeriodSecond}")
	long accessKeyValidPeriodSecond;
	
	@ApiOperation(value = "Get invoice image")
	@GetMapping(value = "/invoice/{invoiceId}/image")
	public ResponseEntity<Object> getImage(@PathVariable String invoiceId, @RequestParam String accessKey) throws Exception {
		
		long period = System.currentTimeMillis() - Long.parseLong(accessKey);		
		
		log.debug("*****Expired period:{}  accessKeyValidPeriodSecond:{}", period/1000L,  accessKeyValidPeriodSecond);	
		
		if(period < 0 || period/1000L > accessKeyValidPeriodSecond) {
			log.debug("*****Invalid accessKey:{}", accessKey);	
			return ResponseEntity.status(HttpStatus.OK).contentType(MediaType.APPLICATION_PROBLEM_JSON_UTF8).body(Result.failure().msg("Invalid accessKey"));
		}
		
		InvoiceImage image = invoiceService.getInvoiceImage(invoiceId);
		
		return ResponseEntity.status(HttpStatus.OK).contentType(new MediaType("image",image.getMime())).body(image.getImage());
	}

	@ApiOperation(value = "Download a list of invoice images")
	@PostMapping(value = "/invoice/image/download")
	public ResponseEntity<byte[]> getImages(@RequestBody Map<String/*invoiceId*/,String/*tag*/> invoiceIdTagMap) throws Exception {
			
		log.debug("*****invoice ids={}, tags={}",invoiceIdTagMap.keySet(), invoiceIdTagMap.values());
		
		ByteArrayOutputStream baos = new ByteArrayOutputStream();			
		ZipOutputStream zipOs = new ZipOutputStream(baos);
		
		List<InvoiceImage> images = invoiceService.getInvoiceImages(Arrays.asList(invoiceIdTagMap.keySet().toArray(new String[]{})));					
		for(InvoiceImage image : images) {
			zipOs.putNextEntry(new ZipEntry(invoiceIdTagMap.get(image.getInvoiceId()) +"-"+ image.getInvoiceId() +"."+ image.getMime()));
			zipOs.write(image.getImage());
			zipOs.closeEntry();	
		}
		zipOs.close();
			
		String filename = new StringBuffer("snapex-invoice-").append(DateFormatUtils.format(System.currentTimeMillis(), "yyyy-MM-dd_HH_mm_ss")).append(".zip").toString();
		
		return ResponseEntity.ok()
				.contentType(MediaType.APPLICATION_OCTET_STREAM)
				.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename="+filename)
				.body(baos.toByteArray());		
	}	
}
